package com.xored.glance.internal.core;

import java.util.ArrayList;
import java.util.List;

import org.eclipse.jface.action.IMenuManager;
import org.eclipse.jface.action.Separator;
import org.eclipse.jface.dialogs.PopupDialog;
import org.eclipse.jface.layout.GridDataFactory;
import org.eclipse.jface.viewers.ArrayContentProvider;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.viewers.TableViewer;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.StyleRange;
import org.eclipse.swt.events.KeyEvent;
import org.eclipse.swt.events.KeyListener;
import org.eclipse.swt.events.ModifyEvent;
import org.eclipse.swt.events.ModifyListener;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableItem;
import org.eclipse.swt.widgets.Text;

import com.xored.glance.core.IMatch;
import com.xored.glance.core.ITextBlock;
import com.xored.glance.core.ITextContainer;
import com.xored.glance.internal.actions.CheckAction;
import com.xored.glance.internal.items.ItemCell;
import com.xored.glance.internal.items.ItemDecorator;
import com.xored.glance.internal.tables.TableItemProvider;
import com.xored.glance.internal.utils.UIUtils;
import com.xored.glance.search.ISearchPanel;
import com.xored.glance.search.SearchManager;
import com.xored.glance.search.SearchUnit;
import com.xored.glance.search.engine.Match;

public class PopupSearchDialog extends PopupDialog implements ISearchPanel {

	private static final Color BAD_COLOR = new Color(Display.getDefault(), 255,
			102, 102);
	private Text titleText;
	private SearchManager manager;
	private TableViewer viewer;
	private ITextContainer textControl;
	protected boolean highlight;
	private static final String HELP_TEXT = "Enter search text";

	public PopupSearchDialog(Shell parent, ITextContainer textControl) {
		super(parent, SWT.RESIZE, true, true, false, true, true, null, null);
		this.textControl = textControl;
		setInfoText(HELP_TEXT);
		highlight = GlancePlugin.getDefault().getPluginPreferences()
				.getBoolean(IConstants.HIGHLIGHT);
		manager = new SearchManager(this);
	}

	@Override
	protected int getShellStyle() {
		return super.getShellStyle() | SWT.TRANSPARENT;
	}

	private Control tma;

	@Override
	protected Control createTitleMenuArea(Composite parent) {
		tma = super.createTitleMenuArea(parent);
		return tma;
	}

	@Override
	protected Control getFocusControl() {
		return titleText;
	}

	@Override
	protected Control createTitleControl(Composite parent) {
		titleText = new Text(parent, SWT.NONE);
		titleText.addModifyListener(new ModifyListener() {

			public void modifyText(ModifyEvent e) {
				if (tma != null) {
					manager.find(titleText.getText(), SearchManager.FIND_HERE);
				}
			}
		});
		titleText.addKeyListener(new KeyListener() {

			public void keyReleased(KeyEvent e) {
			}

			public void keyPressed(KeyEvent e) {
				if (e.keyCode == '\r') {
					if (e.stateMask == 0)
						findNext();
					else if (e.stateMask == SWT.SHIFT)
						findPrevious();
				}
			}

		});

		GridDataFactory.fillDefaults().align(SWT.FILL, SWT.CENTER).grab(true,
				false).span(1, 1).applyTo(titleText);

		return titleText;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * org.eclipse.jface.dialogs.PopupDialog#createDialogArea(org.eclipse.swt
	 * .widgets.Composite)
	 */
	@Override
	protected Control createDialogArea(Composite parent) {
		Composite composite = (Composite) super.createDialogArea(parent);
		viewer = new TableViewer(composite, SWT.SINGLE | SWT.H_SCROLL
				| SWT.V_SCROLL);
		viewer.setContentProvider(new ArrayContentProvider());
		viewer.setLabelProvider(new MatchLabelProvider());

		Table table = viewer.getTable();
		new ItemDecorator(table, TableItemProvider.getInstance(),
				SWT.BACKGROUND | SWT.FOREGROUND);

		table.setFont(textControl.getFont());
		table.setLinesVisible(false);
		table.setHeaderVisible(false);
		table.setLayoutData(new GridData(GridData.FILL_BOTH));
		return composite;
	}

	private void findNext() {
		if (manager != null) {
			manager.find(titleText.getText(), SearchManager.FIND_NEXT);
		}
	}

	private void findPrevious() {
		if (manager != null) {
			manager.find(titleText.getText(), SearchManager.FIND_PREVIOUS);
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.xored.glance.internal.engine.ISearchPanel#clear()
	 */
	public void clear() {
		UIUtils.asyncExec(getShell(), new Runnable() {
			public void run() {
				applyBackgroundColor(getBackground(), tma);
				textControl.select(null);
			}
		});
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * com.xored.glance.internal.engine.ISearchPanel#find(com.xored.glance.core
	 * .IMatch)
	 */
	public void find(final Match match) {
		UIUtils.asyncExec(getShell(), new Runnable() {
			public void run() {
				Color color = match == null ? BAD_COLOR : getBackground();
				applyBackgroundColor(color, tma);
				textControl.select(createMatch(match));
				if (match != null) {
					viewer.setSelection(new StructuredSelection(
							createDecoration(match)));
				}
				viewer.getTable().showSelection();
			}
		});
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.xored.glance.internal.engine.ISearchPanel#getText()
	 */
	public ITextBlock[] getText() {
		return textControl.getText();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.eclipse.jface.dialogs.PopupDialog#close()
	 */
	@Override
	public boolean close() {
		textControl.dispose();
		return super.close();
	}

	@Override
	protected void fillDialogMenu(IMenuManager dialogMenu) {
		super.fillDialogMenu(dialogMenu);
		dialogMenu.add(new Separator());
		dialogMenu.add(new CheckAction("Highlight", IConstants.HIGHLIGHT,
				"icons/highlight.png") {
			protected void doRun() {
				highlight = isChecked();
				UIUtils.asyncExec(getShell(), new Runnable() {
					public void run() {
						if (highlight && matches != null)
							textControl.show(matches);
						else
							textControl.show(IMatch.EMPTY);
					}
				});
			}
		});
		dialogMenu.add(new CheckAction("Match Case", IConstants.MATCH_CASE,
				"icons/match_case.png") {
			protected void doRun() {
				manager.setMatchCase(isChecked());
			}
		});
		dialogMenu.add(new CheckAction("Word Prefix", IConstants.WORD_PREFIX,
				"icons/whole_word.gif") {
			@Override
			protected void doRun() {
				manager.setPrefix(isChecked());
			}
		});
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * com.xored.glance.internal.engine.ISearchPanel#match(com.xored.glance.
	 * core.IMatch[])
	 */
	public void match(final Match[] newMatches) {
		this.matches = createMatches(newMatches);
		UIUtils.asyncExec(getShell(), new Runnable() {
			public void run() {
				if (matches != null) {
					if (highlight)
						textControl.show(matches);
					setInfoText(matches.length + " matches found");
				} else {
					if (highlight)
						textControl.show(IMatch.EMPTY);
					setInfoText(HELP_TEXT);
				}
				DecoratedText[] decorations = createDecorations(newMatches);
				viewer.getTable().setRedraw(false);
				viewer.getTable().removeAll();
				viewer.setInput(decorations);
				TableItem[] items = viewer.getTable().getItems();
				for (int i = 0; i < items.length && i < decorations.length; i++) {
					TableItem item = items[i];
					String key = ItemCell.KEY_TEXT_LAYOUT + "0";
					item.setData(key, decorations[i].getStyles().getStyles());
				}
				viewer.getTable().setRedraw(true);
			}
		});
	}

	protected IMatch[] createMatches(Match[] matches) {
		if (matches == null)
			return null;

		IMatch[] newMatches = new IMatch[matches.length];
		for (int i = 0; i < newMatches.length; i++) {
			newMatches[i] = createMatch(matches[i]);
		}
		return newMatches;
	}

	protected DecoratedText createDecoration(Match match) {
		SearchUnit block = (SearchUnit) match.getSearchUnit();
		String text = block.getText();

		String lineSeparator = "\n";
		int start = text.lastIndexOf(lineSeparator, match.getOffset());
		if (start < 0)
			start = 0;
		else {
			start += lineSeparator.length();
		}
		int end = text.indexOf(lineSeparator, match.getOffset()
				+ match.getLength());
		if (end < 0)
			end = text.length();
		return new DecoratedText(block, start, end);
	}

	protected DecoratedText[] createDecorations(Match[] matches) {
		if (matches == null)
			return null;
		List<DecoratedText> decorations = new ArrayList<DecoratedText>(
				matches.length);
		DecoratedText decoration = null;
		for (int i = 0; i < matches.length; i++) {
			Match match = matches[i];
			DecoratedText newDecoration = createDecoration(match);
			if (decoration == null || !decoration.equals(newDecoration)) {
				decoration = newDecoration;
				decorations.add(decoration);
			}
			decoration.addStyle(new StyleRange(match.getOffset(), match
					.getLength(), getFgHighlight(), getBgHighlight()));
		}
		return decorations.toArray(new DecoratedText[decorations.size()]);
	}

	protected Color getBgHighlight() {
		return getShell().getDisplay().getSystemColor(SWT.COLOR_YELLOW);
	}

	protected Color getFgHighlight() {
		return getShell().getDisplay()
				.getSystemColor(SWT.COLOR_LIST_FOREGROUND);
	}

	protected IMatch createMatch(Match match) {
		if (match == null)
			return null;
		SearchUnit block = (SearchUnit) match.getSearchUnit();
		return block.getContainer().createMatch(match.getOffset(),
				match.getLength());
	}

	private IMatch[] matches;

}
